#include "mbedtls/platform.h"
#include "mbedtls/net.h"
#include "mbedtls/debug.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
#include "mbedtls/certs.h"
#include "user_http_s.h"
#include "esp_event.h"
#include "os.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include <freertos/FreeRTOS.h>
#include "freertos/task.h"
#include "freertos/queue.h"
#include "cJSON.h"
#include "SYSTEM.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include <arpa/inet.h>
#include "https.h"



/** 
 * 从给定的网址中获取主机域名和域名后面的内容分别存放至HOST和Filename
 * @param[in]   URL     :需要解析的域名地址
 * @param[out]  host    :解析获取域名的主机地址
 * @param[out]  filename:解析获取从域名主机的内容地址
 * @retval      
 *              0: 解析成功
 *             -1: URL为空
 *             -2: host为空
 *             -3: filename为空
 *             -4: 传进来的网址中没有https://和http://
 * @par         示例:
 *              1.如果URL是https://www.sojson.com/open/api/weather/json.shtml?city=深圳
 *              2.host是"www.sojson.com/"
 *              3.filename是"open/api/weather/json.shtml?city=深圳"
 * 
 * @par         修改日志 
 *               Ver0.0.1:
                     Helon_Chan, 2018/06/27, 初始化版本\n 
 */
esp_err_t http_url_parse(char *URL, char *host, char *filename)
{
  char *PA;
  char *PB;
  /* 分别判断传来的实参是不是为空 */
  if (URL == NULL)
  {
    return -1;
  }
  else if (host == NULL)
  {
    return -2;
  }
  else if (filename == NULL)
  {
    return -3;
  }

  /* 初始化传进来的用于存放host和filename的内存空间 */
  os_memset(host, 0, sizeof(host)/sizeof(char));
  os_memset(filename, 0, sizeof(filename)/sizeof(char));
  PA = URL;
  /* 判断传进来的网址是否带有"http://以及https://"内容 */
  if (!os_strncmp(PA, "http://", os_strlen("http://")))
  {
    PA = URL + os_strlen("http://");
  }
  else if (!os_strncmp(PA, "https://", os_strlen("https://")))
  {
    PA = URL + os_strlen("https://");
  }
  else
  {
    return -4;
  }
  /* 获取主页地址后面内容的首地址 */
  PB = os_strchr(PA, '/');
  if (PB)
  {
    /* 获取传进来网址的主页以及主页后面跟着的地址内容 */
    os_memcpy(host, PA, os_strlen(PA) - os_strlen(PB));
    if (PB + 1)
    {
      os_memcpy(filename, PB + 1, os_strlen(PB - 1));
      filename[os_strlen(PB) - 1] = 0;
    }
    host[os_strlen(PA) - os_strlen(PB)] = 0;
  }
  /* 如果传进来的网址是主页则直接获取主页地址内容 */
  else
  {
    os_memcpy(host, PA, os_strlen(PA));
    host[os_strlen(PA)] = 0;
  }
  return ESP_OK;
}


static int recv_timeout_cb( void *ctx, unsigned char *buf, size_t len,
                            uint32_t timeout )
{
  int ret;
  printf("http超时\r\n");
  ret = mbedtls_net_recv_timeout(ctx, buf, len, timeout );
  return ret;
}
            

const struct addrinfo hints = {
    .ai_family = AF_INET,
    .ai_socktype = SOCK_STREAM,
};

int https_get_data_no_head(char *data)
{
  //char *buf=(char*)os_malloc(strlen(data));
  char *str=strstr(data,"\r\n\r\n");
  if(str!=NULL)
  {
    https_DEBUG("找到get头开始去掉\r\n");
    //data = str+4;
    sprintf(data,"%s",str+4);
  }else
  {
    https_DEBUG("未找到get头\r\n");

  }
  //os_free(buf);

  return 0;
}


struct addrinfo *addrinfo_res=NULL;
struct in_addr *inaddr=NULL;
int socket_s=0;
char *recv_buf=NULL;

int http_get_task(char * send_buf,char * send_host,char *read_data)
{


  //printf( "堆内存剩余-0:%d\r\n", esp_get_free_heap_size());
  int return_dat=0;
  int rx_len=0;
  int time=0;
  char buf1[20];
  int len=0;
  char *str=NULL;
  char *str1=NULL;  

#define recv_buf_size 1024*2

  recv_buf=(char*)os_malloc(recv_buf_size);
 // char *mid_buf=(char*)os_malloc(recv_buf_size);
  while(1) 
  {
     // printf( "堆内存剩余-1:%d\r\n", esp_get_free_heap_size());
      http_get_task_DEBUG("开始域名解析\r\n");
      //DNS域名解析
      int err = getaddrinfo(send_host, WEB_PORT, &hints, &addrinfo_res);
      if(err != 0 || addrinfo_res == NULL) 
      {       
        http_get_task_DEBUG("域名解析失败\r\n");
        return_dat=-1;
        break;
      }
     // printf( "堆内存剩余-2:%d\r\n", esp_get_free_heap_size());
      //打印获取的IP
      inaddr = &((struct sockaddr_in *)addrinfo_res->ai_addr)->sin_addr;
      http_get_task_DEBUG("域名解析成功:%s\r\n",inet_ntoa(*inaddr));

      http_get_task_DEBUG("新建socket\r\n");
     
      //新建socket
      socket_s = socket(addrinfo_res->ai_family, addrinfo_res->ai_socktype, 0);
      if(socket_s < 0) 
      {
        http_get_task_DEBUG("新建socket失败:%d\r\n",socket_s);
        return_dat=-2;

        break;          
      }
      http_get_task_DEBUG("新建socket成功\r\n");

    
      http_get_task_DEBUG("连接ip\r\n");
      //连接ip
      if(connect(socket_s, addrinfo_res->ai_addr, addrinfo_res->ai_addrlen) != 0)
      {
        http_get_task_DEBUG("连接ip失败:%d\r\n",errno);
        return_dat=-3;
        break;   
      }
      http_get_task_DEBUG("连接ip成功\r\n");

      http_get_task_DEBUG("发送http包\r\n");
      //发送http包
      if (write(socket_s, send_buf, strlen(send_buf)) < 0)
        {
        http_get_task_DEBUG("发送http包l失败\r\n");
        return_dat=-4;
        break;   
      }
      http_get_task_DEBUG("发送http包成功\r\n");
      //printf( "堆内存剩余-3:%d\r\n", esp_get_free_heap_size());
    
      http_get_task_DEBUG("获取http应答包\r\n");
      //printf( "堆内存剩余-4:%d\r\n", esp_get_free_heap_size());
      //获取http应答包
      // int len1=0;
      // do {
      //     bzero(recv_buf,recv_buf_size);
      //     rx_len = read(socket_s, recv_buf, recv_buf_size-1);
      //     strcat(read_data,recv_buf);
      //     len1+=rx_len;
      // } while(rx_len > 0);
      // http_get_task_DEBUG("获取http应答成功,数据长度:%d\r\n数据内容:----%s----\r\n",len1,read_data);
      // break;

      memset(recv_buf,0,recv_buf_size);    
      int sta=0;
      
      while(1)
      {
          http_get_task_DEBUG("开始获取\r\n"); 
         // printf( "堆内存剩余-41:%d\r\n", esp_get_free_heap_size());
          rx_len += read(socket_s, &recv_buf[rx_len], recv_buf_size-1);
          //printf( "堆内存剩余-42:%d\r\n", esp_get_free_heap_size());       
          if(rx_len>0)
          {
            time=0;  
            http_get_task_DEBUG("获取http应答成功,数据长度:%d\r\n数据内容:----%s----\r\n",rx_len,recv_buf); 

            str=strstr(recv_buf,"Content-Length: ");  
            if(str==NULL||sta==1)
            {
              sta=1;
              http_get_task_DEBUG("数据包中无Content-Length\r\n");

              if(rx_len<6)
              {
                http_get_task_DEBUG("已接收完毕\r\n"); 
                break;
              }
#if 0//已经从外部去掉报头
              if(sta==0)
              {
                str1 =strstr(recv_buf,"\r\n\r\n");
                if(str1!=NULL)
                {
                  sta=1;
                  str1+=4;
                  http_get_task_DEBUG("找到----%s----\r\n",str1);  
                }else
                {
                  http_get_task_DEBUG("未找到\r\n"); 
                  break; 
                }
              }else
              {
                str1=recv_buf;              
              } 
#endif
              strcat(read_data,recv_buf);
              rx_len=0;
              bzero(recv_buf,recv_buf_size);

            }else
            {
              str+=16;             
              memset(buf1,0,sizeof(buf1));
              for(int i=0;*str!='\r';i++)
              {
                buf1[i]=*str;
                str++;
              }            
              len = atoi(buf1);
              http_get_task_DEBUG("报头实体长度:%s %d 接收长度:%d\r\n",buf1,len,rx_len);
              if(rx_len<len)//GET到的数据过长需分多次获取
              {
                http_get_task_DEBUG("获取http长度不够继续接收\r\n");
                continue;
              }else
              {
                //http_get_task_DEBUG("接收成功 长度:%d\r\n----%s----\r\n",rx_len,recv_buf);
#if 0//已经从外部去掉报头
                str1 =strstr(recv_buf,"\r\n\r\n");
                if(str1!=NULL)
                {
                  str1+=4;
                  http_get_task_DEBUG("\r\n找到----%s----\r\n",str1);  
                }
                else
                {
                  str1 = recv_buf;
                }
#endif         
                str1 = recv_buf;
                sprintf(read_data,"%s\r\n",str1);
                http_get_task_DEBUG("复制成功 长度:%d\r\n----%s----\r\n",strlen(read_data),read_data);
              }
              break;
            }            
          }else
          {
            http_get_task_DEBUG("接收完毕\r\n");
            break;
          }       
          if(++time>10)
          {
            http_get_task_DEBUG("获取http应答包失败\r\n");
            return_dat=-5;
            break;   
          }
          http_get_task_DEBUG("正在获取获取http应答包:%d\r\n",time);
          //vTaskDelay(100 / portTICK_RATE_MS);
      }        
      ///printf( "堆内存剩余-5:%d\r\n", esp_get_free_heap_size());
      break;
  }
  close(socket_s);
  freeaddrinfo(addrinfo_res);
  os_free(recv_buf);
  //printf( "堆内存剩余-6:%d\r\n", esp_get_free_heap_size());
  return return_dat;
}

uint8_t https_get_buffer[1024*2];//存放get请求的相关数据
uint8_t  https_recv_buff[1024*2]; //存放接收到GET请求的数据

int HTTP_Read_Data(char *URL,char *read_data,uint8_t method) 
{
  int return_dat=0;
  char http_host[128];
  char http_filename[100];

  if (http_url_parse(URL, http_host, http_filename))
  {
    HTTP_Read_Data_DEBUG("从URL中提取数据错误\r\n");
    return -1;
  }
  HTTP_Read_Data_DEBUG("从URL中提取数据成功\r\n");
  HTTP_Read_Data_DEBUG("http_host:%s\r\n",http_host);
  HTTP_Read_Data_DEBUG("http_filename:%s\r\n",http_filename);

#if 1
  sprintf((char*)https_get_buffer, GET, http_filename, http_host);
  return_dat = http_get_task((char*)https_get_buffer,http_host,read_data);
  https_get_data_no_head(read_data);
  return return_dat;
#else

  int ret,len; 
  
  
  mbedtls_net_context *net_ctx = (mbedtls_net_context *)os_malloc(sizeof(mbedtls_net_context));
  mbedtls_entropy_context *entropy = (mbedtls_entropy_context *)os_malloc(sizeof(mbedtls_entropy_context));
  mbedtls_ctr_drbg_context *ctr_drbg = (mbedtls_ctr_drbg_context *)os_malloc(sizeof(mbedtls_ctr_drbg_context));
  mbedtls_ssl_context *ssl_ctx = (mbedtls_ssl_context *)os_malloc(sizeof(mbedtls_ssl_context));
  mbedtls_ssl_config *ssl_conf = (mbedtls_ssl_config *)os_malloc(sizeof(mbedtls_ssl_config));  


//初始化RNG和会话数据
  mbedtls_net_init(net_ctx);
  mbedtls_ssl_init(ssl_ctx);
  mbedtls_ssl_config_init(ssl_conf);
  mbedtls_ctr_drbg_init(ctr_drbg);

  HTTP_Read_Data_DEBUG( "初始化RNG和会话数据\r\n");

  mbedtls_entropy_init(entropy);
  if ((ret = mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, entropy,
                                   (const unsigned char *)NULL,
                                   0)) != 0)
  {
    HTTP_Read_Data_DEBUG( "mbedtls_ctr_drbg_seed错误:%d\n", ret);
    return_dat=-2;
    goto exit;
  }


//进行TCP连接

  HTTP_Read_Data_DEBUG( "进行TCP连接:主机:%s 端口:%s\r\n", http_host, REMOTE_PORT);
  //fflush( stdout );

  if ((ret = mbedtls_net_connect(net_ctx, http_host,REMOTE_PORT, MBEDTLS_NET_PROTO_TCP)) != 0)
  {
    HTTP_Read_Data_DEBUG( "连接TCP失败:%d\r\n", ret);
    return_dat=-3;
    goto exit;
  }
  HTTP_Read_Data_DEBUG( "连接TCP成功\r\n");

//设置SSL/TLS的结构.
  HTTP_Read_Data_DEBUG( "设置SSL/TLS的结构...\r\n");

  if ((ret = mbedtls_ssl_config_defaults(ssl_conf,
                                         MBEDTLS_SSL_IS_CLIENT,
                                         MBEDTLS_SSL_TRANSPORT_STREAM,
                                         MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
  {
    HTTP_Read_Data_DEBUG( "设置SSL/TLS的结构失败:%d\r\n", ret);
    goto exit;
  }
  HTTP_Read_Data_DEBUG( "设置SSL/TLS的结构成功\r\n");

  HTTP_Read_Data_DEBUG( "heap剩余:%d\r\n", esp_get_free_heap_size());

  /* 由于证书会过期,所以这些不进行证书认证 */
  mbedtls_ssl_conf_authmode(ssl_conf, MBEDTLS_SSL_VERIFY_NONE);

  /* 设置随机数生成的函数及方法 */
  mbedtls_ssl_conf_rng(ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg);
  /* 这里不需要设置调试函数 */
  mbedtls_ssl_conf_dbg(ssl_conf, NULL, NULL);

  /* 将ssl_conf的相关信息填充于ssl_ctx中去,用于进行SSL握手时使用 */
  if ((ret = mbedtls_ssl_setup(ssl_ctx, ssl_conf)) != 0)
  {
    HTTP_Read_Data_DEBUG( "mbedtls_ssl_setup错误:%d\r\n", ret);
    return_dat=-4;
    goto exit;
  }

  if ((ret = mbedtls_ssl_set_hostname(ssl_ctx, "mbed TLS Server")) != 0)
  {
    HTTP_Read_Data_DEBUG( "mbedtls_ssl_set_hostname错误:%d\r\n", ret);
    return_dat=-5;
    goto exit;
  }


  /* 设置发送以及接收的时候,调用的内部函数 */
//  mbedtls_ssl_set_bio(ssl_ctx, net_ctx, mbedtls_net_send, mbedtls_net_recv, recv_timeout_cb);
  /* 设置发送以及接收的时候,调用的内部函数 */
  mbedtls_ssl_set_bio(ssl_ctx, net_ctx, mbedtls_net_send, mbedtls_net_recv, NULL);

  HTTP_Read_Data_DEBUG( "执行SSL/TLS握手...\r\n");


//循环进行握手操作，直到握手成功
  while ((ret = mbedtls_ssl_handshake(ssl_ctx)) != 0)
  {
    if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
    {
      HTTP_Read_Data_DEBUG( "执行SSL/TLS握手失败: -0x%x\r\n", -ret);
      return_dat=-6;
      goto exit;
    }

    HTTP_Read_Data_DEBUG( "正在执行握手...\r\n");
    vTaskDelay(1000 / portTICK_RATE_MS);

  }



 /* while(1)
  {
    ret = mbedtls_ssl_handshake_step(ssl_ctx);
    HTTP_Read_Data_DEBUG( "正在执行握手...%d\r\n",ret);
    if(ret==0)
      break;
    else
    if(ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS)
    {
      continue;
    }else  if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
    {
      HTTP_Read_Data_DEBUG( "执行SSL/TLS握手失败: -0x%x\r\n", -ret);
      return_dat=-6;
      goto exit;
    }   
    vTaskDelay(1000 / portTICK_RATE_MS);
  }
*/

  HTTP_Read_Data_DEBUG( "执行SSL/TLS握手成功...\r\n");

//写GET请求
  HTTP_Read_Data_DEBUG( "写GET请求:\r\n");
  memset(https_get_buffer, 0, sizeof(https_get_buffer));
  if (method == GET_REQ)
  {
    len = sprintf((char*)https_get_buffer, GET, http_filename, http_host);
  }
  else
  {
    len = sprintf((char*)https_get_buffer, POST, http_filename, strlen(POST_CONTENT), http_host, POST_CONTENT);
  }

  while (1)
  {
    ret = mbedtls_ssl_write(ssl_ctx, https_get_buffer, len);
    HTTP_Read_Data_DEBUG( "正在执行写GET请求...%d\r\n",ret);
    if(ret >0)
    {
      break;
    }
    else
    if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
    {
      HTTP_Read_Data_DEBUG( "HTTP写入失败:%d\r\n", ret);
      return_dat=-7;
      goto exit;
    }   
    vTaskDelay(1000 / portTICK_RATE_MS);
  }

  len = ret;
  HTTP_Read_Data_DEBUG( "----\r\n共写入%d字节,写入的内容:\r\n%s\r\n-----", len, (char *)https_get_buffer);

//读取HTTP响应
  HTTP_Read_Data_DEBUG( "读取HTTP响应:\r\n");
  while(1)
  {
    len = sizeof(https_recv_buff) - 1;
    memset(https_recv_buff, 0, sizeof(https_recv_buff));
    ret = mbedtls_ssl_read(ssl_ctx, https_recv_buff, len);

    if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
    {
      HTTP_Read_Data_DEBUG("等待HTTP返回数据...\r\n");
      continue;
    }

    if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)//同行通知我们,连接将被关闭
    {
      HTTP_Read_Data_DEBUG( "MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY\n");
      ret = 0;
      return_dat=-8;
      goto exit;
    }
    if (ret == MBEDTLS_ERR_SSL_TIMEOUT)
    {
      HTTP_Read_Data_DEBUG( "MBEDTLS_ERR_SSL_TIMEOUT\n");
      ret = 0;
      return_dat=MBEDTLS_ERR_SSL_TIMEOUT;
      goto exit;
    }

    if (ret < 0)
    {
      HTTP_Read_Data_DEBUG( "HTTP错误:%d\r\n", ret);
      return_dat=-9;
      goto exit;
    }
    if (ret == 0)
    {
      HTTP_Read_Data_DEBUG( "EOF\r\n");      
      return_dat=-10;
      goto exit;
    }
    else
    {

      HTTP_Read_Data_DEBUG("GET到的原始数据:长度:%d\r\n-----\r\n%s\r\n-----\r\n",ret,https_recv_buff);
      https_get_data_no_head((char*)https_recv_buff);
      HTTP_Read_Data_DEBUG("处理后的数据:长度:%d\r\n-----\r\n%s\r\n-----\r\n",ret,https_recv_buff);
      len = ret;
      sprintf(read_data,"%s",https_recv_buff);

      

      return_dat=0;
      goto exit;
    }
  }

exit:
  mbedtls_ssl_close_notify(ssl_ctx);
  mbedtls_net_free(net_ctx);
  mbedtls_ssl_free(ssl_ctx);
  mbedtls_ssl_config_free(ssl_conf);
  mbedtls_ctr_drbg_free(ctr_drbg);
  mbedtls_entropy_free(entropy);

  os_free(ssl_ctx);
  os_free(net_ctx);
  os_free(ctr_drbg);

  os_free(entropy);
  os_free(ssl_conf);
  HTTP_Read_Data_DEBUG("----释放内存成功----\r\n");

  return return_dat;
#endif

}